package com.zmn.oms.services.impl.report;

import com.zmn.oms.business.interfaces.es.OrderWorkEsBService;
import org.apache.dubbo.config.annotation.Reference;
import com.google.common.collect.Lists;
import com.zmn.common.constant.GlobalConsts;
import com.zmn.common.dto2.ResponseDTO;
import com.zmn.common.utils.date.DateUtil;
import com.zmn.common.utils.pager.Pages;
import com.zmn.mcc.common.dto.staff.StaffDRO;
import com.zmn.mcc.dubbo.dto.DubboConsts;
import com.zmn.mcc.dubbo.interfaces.staff.StaffListRemoteService;
import com.zmn.oms.common.constant.OrderConsts;
import com.zmn.oms.common.constant.OrderStatusConsts;
import com.zmn.oms.model.entity.report.ReportOrderedQuery;
import com.zmn.oms.model.vo.report.ReportOrderedVO;
import com.zmn.oms.services.interfaces.report.ReportOrderedService;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.filter.ParsedFilter;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.metrics.ParsedSum;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 类描述：录单报表业务实现
 *
 * @author hankangshuai
 * @since 2019-7-9
 */
@SuppressWarnings("ALL")
@Service
public class ReportOrderedServiceImpl implements ReportOrderedService {

    @Resource
    OrderWorkEsBService orderWorkEsBService;

    @Reference(version = DubboConsts.INTERFACE_VERSION, check = false)
    StaffListRemoteService staffListRemoteService;

    @Override
    public List<ReportOrderedVO> listPageByQuery(ReportOrderedQuery query) {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        NativeSearchQuery build = nativeSearchQueryBuilder.addAggregation(getAggregation(query)).addAggregation(AggregationBuilders.terms("totalCount").field("receiverId").size(10000))
                .withQuery(getFilter(query))
                .withPageable(PageRequest.of(0, 1))
                .build();
        Aggregations aggregations = orderWorkEsBService.getAggregationsByQuery(build);
        ParsedLongTerms totalCounta = aggregations.get("totalCount");
        int bucketSize = totalCounta.getBuckets().size();
        query.setTotalCount(bucketSize);
        ParsedLongTerms receiverIdTerms = aggregations.get("receiverIdTerms");
        List<? extends Terms.Bucket> buckets = receiverIdTerms.getBuckets();
        List<ReportOrderedVO> reportOrderedVOS =  buckets.stream().filter(bucket -> Objects.nonNull(bucket.getKeyAsNumber())).map(bucket -> {
                ReportOrderedVO reportOrderedVO = new ReportOrderedVO();
                int receiverId = bucket.getKeyAsNumber().intValue();
                reportOrderedVO.setReceiverId(receiverId);
                Long totalCount = bucket.getDocCount();
                reportOrderedVO.setTotalCount(totalCount.intValue());
                Aggregations dataAgg = bucket.getAggregations();
                Long newCount = ((ParsedFilter) dataAgg.get("newCountFilter")).getDocCount();
                reportOrderedVO.setNewCount(newCount.intValue());
                Long reworkCount = ((ParsedFilter) dataAgg.get("reworkCountFilter")).getDocCount();
                reportOrderedVO.setReworkCount(reworkCount.intValue());
                Long newWithDuplicateCount = ((ParsedFilter) dataAgg.get("newWithDuplicateCountFilter")).getDocCount();
                reportOrderedVO.setNewWithDuplicateCount(newWithDuplicateCount.intValue());
                Long reworkWithDuplicateCount = ((ParsedFilter) dataAgg.get("reworkWithDuplicateCountFilter")).getDocCount();
                reportOrderedVO.setReworkWithDuplicateCount(reworkWithDuplicateCount.intValue());
                Long doingCount = ((ParsedFilter) dataAgg.get("doingCountFilter")).getDocCount();
                reportOrderedVO.setDoingCount(doingCount.intValue());
                Long discardCount = ((ParsedFilter) dataAgg.get("discardCountFilter")).getDocCount();
                reportOrderedVO.setDiscardCount(discardCount.intValue());
                Long failCount = ((ParsedFilter) dataAgg.get("failCountFilter")).getDocCount();
                reportOrderedVO.setFailCount(failCount.intValue());
                Long successCount = ((ParsedFilter) dataAgg.get("successCountFilter")).getDocCount();
                reportOrderedVO.setSuccessCount(successCount.intValue());
                ParsedSum totalAmountFilter = ((ParsedFilter) dataAgg.get("totalAmountFilter")).getAggregations().get("totalAmountSum");
                int totalAmountSum = (int) totalAmountFilter.getValue();
                reportOrderedVO.setTotalAmountSum(totalAmountSum);
                reportOrderedVO.setTotalNewCount(reportOrderedVO.getNewCount() + reportOrderedVO.getNewWithDuplicateCount());
                reportOrderedVO.generateRate();
                return reportOrderedVO;
            }).collect(Collectors.toList());
        //截取分页前段
        int size = reportOrderedVOS.size();
        if (size == 0) {
            return Collections.emptyList();
        }
        reportOrderedVOS = reportOrderedVOS.stream().skip(query.getStartIndex()).limit(query.getPageSize()).collect(Collectors.toList());
        //补全录单人名称及部门
        List<Integer> recId = reportOrderedVOS.stream().map(ReportOrderedVO::getReceiverId).collect(Collectors.toList());
        Map<Integer, StaffDRO> staffDROMap = Lists.partition(recId, 100).stream().map(recIdList -> {
            ResponseDTO<List<StaffDRO>> listResponseDTO = staffListRemoteService.listStaffByStaffIds(recIdList, null);
            if (!listResponseDTO.isSuccess()) {
                throw new RuntimeException("员工远程接口调用失败");
            }
            return Optional.ofNullable(listResponseDTO.getData()).orElseGet(Collections::emptyList);
        }).flatMap(Collection::stream).collect(Collectors.toMap(StaffDRO::getStaffId, staffDRO -> staffDRO));

        reportOrderedVOS.forEach(reportOrderedVO -> {
            StaffDRO staffDRO = staffDROMap.get(reportOrderedVO.getReceiverId());
            if (Objects.isNull(staffDRO)) {
                reportOrderedVO.setReceiverName("未知员工：" + reportOrderedVO.getReceiverId());
                return;
            }
            reportOrderedVO.setDeptId(staffDRO.getDeptId());
            reportOrderedVO.setReceiverName(staffDRO.getRealName());
        });

        return reportOrderedVOS;
    }

    @Override
    public ReportOrderedVO getTotalLine(ReportOrderedQuery query) {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        NativeSearchQuery build = nativeSearchQueryBuilder.withQuery(getFilter(query))
                .withPageable(Pageable.unpaged())
                .addAggregation(AggregationBuilders.filter("newCountFilter", QueryBuilders.boolQuery()
                        .filter(QueryBuilders.termQuery("type", OrderConsts.ORDER_TYPE_NEW))
                        .filter(QueryBuilders.termQuery("isDuplicate", false))))
                .addAggregation(AggregationBuilders.filter("reworkCountFilter", QueryBuilders.boolQuery()
                        .filter(QueryBuilders.termQuery("type", OrderConsts.ORDER_TYPE_REWORK))
                        .filter(QueryBuilders.termQuery("isDuplicate", false))))
                .addAggregation(AggregationBuilders.filter("newWithDuplicateCountFilter", QueryBuilders.boolQuery()
                        .filter(QueryBuilders.termQuery("type", OrderConsts.ORDER_TYPE_NEW))
                        .filter(QueryBuilders.termQuery("isDuplicate", true))))
                .addAggregation(AggregationBuilders.filter("reworkWithDuplicateCountFilter", QueryBuilders.boolQuery()
                        .filter(QueryBuilders.termQuery("type", OrderConsts.ORDER_TYPE_REWORK))
                        .filter(QueryBuilders.termQuery("isDuplicate", true))))
                .addAggregation(AggregationBuilders.filter("doingCountFilter", QueryBuilders.boolQuery()
                        .filter(QueryBuilders.termQuery("resultStatus", OrderStatusConsts.WORK_RESULT_DOING))
                        .mustNot(QueryBuilders.termQuery("type", OrderConsts.ORDER_TYPE_REFUND))))
                .addAggregation(AggregationBuilders.filter("discardCountFilter", QueryBuilders.boolQuery()
                        .filter(QueryBuilders.termQuery("resultStatus", OrderStatusConsts.WORK_RESULT_DISCARD))
                        .mustNot(QueryBuilders.termQuery("type", OrderConsts.ORDER_TYPE_REFUND))))
                .addAggregation(AggregationBuilders.filter("failCountFilter", QueryBuilders.boolQuery()
                        .filter(QueryBuilders.termQuery("resultStatus", OrderStatusConsts.WORK_RESULT_FAIL))
                        .mustNot(QueryBuilders.termQuery("type", OrderConsts.ORDER_TYPE_REFUND))))
                .addAggregation(AggregationBuilders.filter("successCountFilter", QueryBuilders.boolQuery()
                        .filter(QueryBuilders.termQuery("resultStatus", OrderStatusConsts.WORK_RESULT_SUCCESS))
                        .mustNot(QueryBuilders.termQuery("type", OrderConsts.ORDER_TYPE_REFUND))))
                .addAggregation(AggregationBuilders.filter("totalAmountFilter", QueryBuilders.existsQuery("accountTime"))
                        .subAggregation(AggregationBuilders.sum("totalAmountSum").field("totalAmount")))
                .build();
        Aggregations aggregations = orderWorkEsBService.getAggregationsByQuery(build);
        ReportOrderedVO totalLine = new ReportOrderedVO();
        totalLine.setReceiverName("总计");
        totalLine.setTotalCount(build.getMaxResults());
        Long newCount = ((ParsedFilter) aggregations.get("newCountFilter")).getDocCount();
        totalLine.setNewCount(newCount.intValue());
        Long reworkCount = ((ParsedFilter) aggregations.get("reworkCountFilter")).getDocCount();
        totalLine.setReworkCount(reworkCount.intValue());
        Long newWithDuplicateCount = ((ParsedFilter) aggregations.get("newWithDuplicateCountFilter")).getDocCount();
        totalLine.setNewWithDuplicateCount(newWithDuplicateCount.intValue());
        Long reworkWithDuplicateCount = ((ParsedFilter) aggregations.get("reworkWithDuplicateCountFilter")).getDocCount();
        totalLine.setReworkWithDuplicateCount(reworkWithDuplicateCount.intValue());
        Long doingCount = ((ParsedFilter) aggregations.get("doingCountFilter")).getDocCount();
        totalLine.setDoingCount(doingCount.intValue());
        Long discardCount = ((ParsedFilter) aggregations.get("discardCountFilter")).getDocCount();
        totalLine.setDiscardCount(discardCount.intValue());
        Long failCount = ((ParsedFilter) aggregations.get("failCountFilter")).getDocCount();
        totalLine.setFailCount(failCount.intValue());
        Long successCount = ((ParsedFilter) aggregations.get("successCountFilter")).getDocCount();
        totalLine.setSuccessCount(successCount.intValue());
        double totalAmountSum = ((ParsedSum) ((ParsedFilter) aggregations.get("totalAmountFilter")).getAggregations().get("totalAmountSum")).getValue();
        totalLine.setTotalAmountSum((int) totalAmountSum);
        totalLine.setTotalNewCount(totalLine.getNewCount() + totalLine.getNewWithDuplicateCount());
        totalLine.generateRate();
        return totalLine;
    }

    private AbstractAggregationBuilder getAggregation(Pages query) {
        return AggregationBuilders.terms("receiverIdTerms").field("receiverId")
                //form+size分页
                .size(query.getPageIndex() * query.getPageSize())
                .shardSize(10000)
                //按新单排序
                .order(BucketOrder.aggregation("newCountFilter.doc_count", false))
                .subAggregation(AggregationBuilders.filter("newCountFilter", QueryBuilders.boolQuery()
                        .filter(QueryBuilders.termQuery("type", OrderConsts.ORDER_TYPE_NEW))
                        .filter(QueryBuilders.termQuery("isDuplicate", false))))
                .subAggregation(AggregationBuilders.filter("reworkCountFilter", QueryBuilders.boolQuery()
                        .filter(QueryBuilders.termQuery("type", OrderConsts.ORDER_TYPE_REWORK))
                        .filter(QueryBuilders.termQuery("isDuplicate", false))))
                .subAggregation(AggregationBuilders.filter("newWithDuplicateCountFilter", QueryBuilders.boolQuery()
                        .filter(QueryBuilders.termQuery("type", OrderConsts.ORDER_TYPE_NEW))
                        .filter(QueryBuilders.termQuery("isDuplicate", true))))
                .subAggregation(AggregationBuilders.filter("reworkWithDuplicateCountFilter", QueryBuilders.boolQuery()
                        .filter(QueryBuilders.termQuery("type", OrderConsts.ORDER_TYPE_REWORK))
                        .filter(QueryBuilders.termQuery("isDuplicate", true))))
                .subAggregation(AggregationBuilders.filter("doingCountFilter", QueryBuilders.boolQuery()
                        .filter(QueryBuilders.termQuery("resultStatus", OrderStatusConsts.WORK_RESULT_DOING))
                        .mustNot(QueryBuilders.termQuery("type", OrderConsts.ORDER_TYPE_REFUND))))
                .subAggregation(AggregationBuilders.filter("discardCountFilter", QueryBuilders.boolQuery()
                        .filter(QueryBuilders.termQuery("resultStatus", OrderStatusConsts.WORK_RESULT_DISCARD))
                        .mustNot(QueryBuilders.termQuery("type", OrderConsts.ORDER_TYPE_REFUND))))
                .subAggregation(AggregationBuilders.filter("failCountFilter", QueryBuilders.boolQuery()
                        .filter(QueryBuilders.termQuery("resultStatus", OrderStatusConsts.WORK_RESULT_FAIL))
                        .mustNot(QueryBuilders.termQuery("type", OrderConsts.ORDER_TYPE_REFUND))))
                .subAggregation(AggregationBuilders.filter("successCountFilter", QueryBuilders.boolQuery()
                        .filter(QueryBuilders.termQuery("resultStatus", OrderStatusConsts.WORK_RESULT_SUCCESS))
                        .mustNot(QueryBuilders.termQuery("type", OrderConsts.ORDER_TYPE_REFUND))))
                .subAggregation(AggregationBuilders.filter("totalAmountFilter", QueryBuilders.existsQuery("accountTime"))
                        .subAggregation(AggregationBuilders.sum("totalAmountSum").field("totalAmount")));
    }

    private QueryBuilder getFilter(ReportOrderedQuery query) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        List<QueryBuilder> filter = boolQueryBuilder.filter();
        if (Objects.nonNull(query.getBypass()) && query.getBypass()) {
            boolQueryBuilder.mustNot(QueryBuilders.termQuery("companyId", 0));
        } else if (Objects.nonNull(query.getBypass()) && !query.getBypass()) {
            filter.add(QueryBuilders.termQuery("companyId", 0));
        }
        filter.add(QueryBuilders.termQuery("plat", GlobalConsts.PLAT_MARK_ZMN));
        if (Objects.nonNull(query.getPlatWork())) {
            filter.add(QueryBuilders.termQuery("platWork", query.getPlatWork()));
        }
        //！！注意，此处如集合为空也要加入查询,否则部门下无员工时会查所有员工
        if (Objects.nonNull(query.getReceiverId())) {
            filter.add(QueryBuilders.termsQuery("receiverId", query.getReceiverId()));
        }
        if (Objects.nonNull(query.getCompanyId())) {
            filter.add(QueryBuilders.termQuery("companyId", query.getCompanyId()));
        }
        if (Objects.nonNull(query.getChannelId())) {
            filter.add(QueryBuilders.termQuery("channelId", query.getChannelId()));
        }
        if (Objects.nonNull(query.getStartDate()) && Objects.nonNull(query.getEndDate())) {
            filter.add(QueryBuilders.rangeQuery("inputTime").from(DateUtil.toString(query.getStartDate())).to(DateUtil.toString(query.getEndDate())));
        }
        if (Objects.nonNull(query.getExcludeReceiverId())) {
            boolQueryBuilder.mustNot(QueryBuilders.termsQuery("receiverId", query.getExcludeReceiverId()));
        }
        return boolQueryBuilder;
    }

}